'''
copyright: Copyright (C) 2015-2024, Wazuh Inc.

           Created by Wazuh, Inc. <info@wazuh.com>.

           This program is free software; you can redistribute it and/or modify it under the terms of GPLv2

type: integration

brief: Wazuh is able to detect vulnerabilities in the applications installed in agents using the Vulnerability Detector
       module. This software audit is performed through the integration of vulnerability feeds indexed by Redhat,
       Canonical, Debian, SUSE, Amazon Linux and NVD Database.

components:
    - vulnerability_detector

suite: scan_results

targets:
    - manager

daemons:
    - wazuh-modulesd
    - wazuh-db
    - wazuh-analysisd

os_platform:
    - linux

os_version:
    - Arch Linux
    - Amazon Linux 2022
    - Amazon Linux 2
    - Amazon Linux 1
    - CentOS 8
    - CentOS 7
    - Debian Buster
    - Red Hat 8
    - Ubuntu Focal
    - Ubuntu Bionic
    - Suse Enterprise Linux Server 15
    - Suse Enterprise Linux Server 12
    - Suse Enterprise Linux Server 11
    - Suse Enterprise Linux Desktop 15
    - Suse Enterprise Linux Desktop 12
    - Suse Enterprise Linux Desktop 11

references:
    - https://documentation.wazuh.com/current/user-manual/capabilities/vulnerability-detection/index.html
    - https://documentation.wazuh.com/current/user-manual/reference/ossec-conf/vuln-detector.html#provider

tags:
    - settings
    - vulnerability
    - vulnerability_detector
    - providers
'''
import pytest
from pathlib import Path

from .utils import update_feed_path_configurations
from wazuh_testing import FEEDS_PATH
from wazuh_testing.constants.daemons import ANALYSISD_DAEMON, MODULES_DAEMON, SYSCHECK_DAEMON
from wazuh_testing.utils.file import read_yaml
from wazuh_testing.utils.time import get_current_timestamp
from wazuh_testing.utils.db_queries import agent_db, cve_db
from wazuh_testing.tools.monitors.file_monitor import FileMonitor
from wazuh_testing.constants.paths.logs import WAZUH_LOG_PATH
from wazuh_testing.utils.callbacks import generate_callback
from wazuh_testing.utils.configuration import get_test_cases_data
from wazuh_testing.modules.modulesd.vulnerability_detector import patterns as cb
from wazuh_testing.modules.modulesd.configuration import MODULESD_DEBUG
from wazuh_testing.modules.monitord.configuration import MONITORD_ROTATE_LOG
from test_vulnerability_detector import utils as evm
from . import TEST_CASES_PATH, CONFIGURATIONS_PATH


pytest.skip("The tests will be deprecated, they test the old Vulnerability Detector.", allow_module_level=True)

# Variables
local_internal_options = {MODULESD_DEBUG: '2', MONITORD_ROTATE_LOG: '0'}
daemons_handler_configuration = {'daemons': [ANALYSISD_DAEMON, MODULES_DAEMON, SYSCHECK_DAEMON]}
pytestmark = [pytest.mark.server]

# Configuration and cases data
configurations_path = Path(CONFIGURATIONS_PATH, 'configuration_scan_vulnerability_removal.yaml')
cases_path = Path(TEST_CASES_PATH, 'cases_scan_vulnerability_removal.yaml')

# Test configurations
test_configurations = read_yaml(configurations_path)
configuration_parameters, configuration_metadata, case_ids = get_test_cases_data(cases_path)
test_configurations = update_feed_path_configurations(test_configurations, configuration_metadata, FEEDS_PATH)
systems = [metadata['system'] for metadata in configuration_metadata]


@pytest.mark.tier(level=1)
@pytest.mark.parametrize('test_configuration, test_metadata, agent_system',
                         zip(test_configurations, configuration_metadata, systems), ids=case_ids)
def test_vulnerability_removal_update_package(test_configuration, test_metadata, agent_system, set_wazuh_configuration,
                                              configure_local_internal_options, truncate_monitored_files,
                                              clean_cve_tables, prepare_full_scan_with_vuln_packages,
                                              daemons_handler):
    '''
    description: Check that the Vulnerability Detector module generates an alert when a vulnerability is removed from
                 the inventory when a package is updated to a non-vulnerable version.

    test_phases:
        - Setup:
            - Set a custom Wazuh configuration, with custom feeds for OVAL and NVD.
            - Mock an agent with a custom system and vulnerable packages.
            - Force a partial scan setting the last full scan DB data.
            - Truncate log files
            - Restart wazuh-modulesd.
        - Test:
            - Wait for full scan event log.
            - Update one vulnerable package to a non-vulnerable version.
            - Force again a full scan and wait for the full scan event log.
            - Check that vulnerability removal has been detected (in log) and check for the removal alert.
        - Teardown:
            - Truncate log files
            - Clean CVE tables
            - Remove mocked agent
            - Restore configuration files
            - Restart wazuh-modulesd

    wazuh_min_version: 4.4.0

    tier: 1

    parameters:
        - test_configuration:
            type: dict
            brief: Wazuh configuration data. Needed for set_wazuh_configuration fixture.
        - test_metadata:
            type: dict
            brief: Wazuh configuration metadata
        - agent_system:
            type: str
            brief: System to set to the mocked agent.
        - set_wazuh_configuration:
            type: fixture
            brief: Set the wazuh configuration according to the configuration data.
        - configure_local_internal_options:
            type: fixture
            brief: Set local_internal_options configuration.
        - truncate_monitored_files:
            type: fixture
            brief: Truncate all the log files and json alerts files before and after the test execution.
        - clean_cve_tables:
            type: fixture
            brief: Clean all CVE tables.
        - prepare_full_scan_with_vuln_packages:
            type: fixture
            brief: Setup the initial test state.
        - daemons_handler:
            type: fixture
            brief: Restart the wazuh-modulesd daemon.


    assertions:
        - The full scan finish for the agent.
        - The package is no more vulnerable.
        - The package was eliminated.

    input_description:
        -  The `case_alert_vulnerability_removal.yaml` file provides the module configuration for this test.

    expected_output:
        - 'Finished vulnerability assessment for agent <agent_id>'
        - '<test_package_cve> affecting <test_package_name> was eliminated'
        - Package '<test_package_name>' not vulnerable to '<test_package_cve>'
    '''
    agent_id = prepare_full_scan_with_vuln_packages
    log_monitor = FileMonitor(WAZUH_LOG_PATH)
    package_name = test_metadata['package_1_name']
    cve = test_metadata['package_1_cve']

    # Wait for full scan event log
    evm.check_vuln_assesment_finished(agent_id=agent_id, log_monitor=log_monitor)

    # Update test package 1 to a non-vulnerable version.
    agent_db.update_package(agent_id=agent_id, package=package_name,
                            version=test_metadata['package_version_not_vulnerable'])

    # Force a full scan again after simulating the update the NVD feed and passing the min_full_scan_interval
    cve_db.update_nvd_metadata_vuldet(int(get_current_timestamp()))

    # Check again the full scan event
    evm.check_vuln_assesment_finished(agent_id=agent_id, log_monitor=log_monitor)

    # Ensure the test package update 1 has generated the log.
    log_monitor.start(callback=generate_callback(regex=cb.PACKAGE_NOT_VULNERABLE_TO_CVE,
                                                 replacement={"package": package_name, "cve": cve}))
    assert log_monitor.callback_result is not None, f"Expected {cb.PACKAGE_NOT_VULNERABLE_TO_CVE} event not found."

    # Ensure the test package update 1 has generated an alert.
    log_monitor.start(callback=generate_callback(regex=cb.CVE_AFFECTING_PACKAGE_SOLVED,
                                                 replacement={"package": package_name, "cve": cve}))
    assert log_monitor.callback_result is not None, f"Expected {cb.CVE_AFFECTING_PACKAGE_SOLVED} event not found."


@pytest.mark.tier(level=1)
@pytest.mark.parametrize('test_configuration, test_metadata, agent_system',
                         zip(test_configurations, configuration_metadata, systems), ids=case_ids)
def test_vulnerability_removal_delete_package(test_configuration, test_metadata, agent_system, set_wazuh_configuration,
                                              configure_local_internal_options, truncate_monitored_files,
                                              clean_cve_tables, prepare_full_scan_with_vuln_packages, daemons_handler):
    '''
    description: Check that the Vulnerability Detector module generates an alert when a vulnerability is removed from
                 the inventory.

    test_phases:
        - Setup:
            - Set a custom Wazuh configuration, with custom feeds for OVAL and NVD.
            - Mock an agent with a custom system and vulnerable packages.
            - Force a partial scan setting the last full scan DB data.
            - Truncate log files
            - Restart wazuh-modulesd.
        - Test:
            - Wait for full scan event log.
            - Delete one vulnerable package.
            - Force again a full scan and wait for the full scan event log.
            - Check for vulnerability removal alert.
        - Teardown:
            - Truncate log files
            - Clean CVE tables
            - Remove mocked agent
            - Restore configuration files
            - Restart wazuh-modulesd

    wazuh_min_version: 4.4.0

    tier: 1

    parameters:
        - test_configuration:
            type: dict
            brief: Wazuh configuration data. Needed for set_wazuh_configuration fixture.
        - test_metadata:
            type: dict
            brief: Wazuh configuration metadata
        - agent_system:
            type: str
            brief: System to set to the mocked agent.
        - set_wazuh_configuration:
            type: fixture
            brief: Set the wazuh configuration according to the configuration data.
        - configure_local_internal_options:
            type: fixture
            brief: Set local_internal_options configuration.
        - truncate_monitored_files:
            type: fixture
            brief: Truncate all the log files and json alerts files before and after the test execution.
        - clean_cve_tables:
            type: fixture
            brief: Clean all CVE tables.
        - prepare_full_scan_with_vuln_packages:
            type: fixture
            brief: Setup the initial test state.
        - daemons_handler:
            type: fixture
            brief: Restart the wazuh-modulesd daemon.

    assertions:
        - The full scan finish for the agent.
        - The package is no more vulnerable.
        - The package was eliminated.

    input_description:
        -  The `case_alert_vulnerability_removal.yaml` file provides the module configuration for this test.

    expected_output:
        - 'Finished vulnerability assessment for agent <agent_id>'
        - '<test_package_cve> affecting <test_package_name> was eliminated'
        - Package '<test_package_name>' not vulnerable to '<test_package_cve>'
    '''
    agent_id = prepare_full_scan_with_vuln_packages
    log_monitor = FileMonitor(WAZUH_LOG_PATH)
    package_name = test_metadata['package_0_name']
    cve = test_metadata['package_0_cve']

    # Wait for full scan event log
    evm.check_vuln_assesment_finished(agent_id=agent_id, log_monitor=log_monitor)

    # Delete test package 0.
    agent_db.delete_package(agent_id=agent_id, package=package_name)

    # Force a full scan again after simulating the update the NVD feed and passing the min_full_scan_interval
    cve_db.update_nvd_metadata_vuldet(int(get_current_timestamp()))

    # Check again the full scan event
    evm.check_vuln_assesment_finished(agent_id=agent_id, log_monitor=log_monitor)

    # Ensure the removal of test package 0 has generated an alert.
    log_monitor.start(callback=generate_callback(regex=cb.CVE_AFFECTING_PACKAGE_SOLVED,
                                                 replacement={"package": package_name, "cve": cve}))
    assert log_monitor.callback_result is not None, f"Expected {cb.CVE_AFFECTING_PACKAGE_SOLVED} event not found."
